package com.demo.utils;


import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

public class HttpUtil {

	private static final String GET = "GET";
	private static final String POST = "POST";


	private static ExecutorService executorService;

	static {
		if (executorService != null) {

			Runtime.getRuntime().addShutdownHook(new Thread() {
				@Override
				public void run() {

					executorService.shutdown();
				}

			});
		}
	}

	public static ExecutorService getExecutorService() {
		if (executorService == null) {
			executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() << 1);
		}
		return executorService;
	}


	//同步start----------------
	public static String get(String url, String body) {

		return httpRequestToString(url, new HashMap<>(), GET, body);
	}


	public static String get(String url, Map body) {

		return httpRequestToString(url, new HashMap<>(), GET, map2String(body));
	}


	public static String get(Map<String, String> headers, String url, String body) {

		return httpRequestToString(url, headers, GET, body);
	}

	public static String get(Map<String, String> headers, String url, Map body) {

		return httpRequestToString(url, headers, GET, map2String(body));
	}

	public static String get(Map<String, String> headers, String url) {

		return httpRequestToString(url, headers, GET, null);
	}

	public static String get(String url) {

		return httpRequestToString(url, new HashMap<>(), GET, null);
	}

	public static File get2File(String url, String body) {

		return httpRequestToFile(url, new HashMap<>(), GET, body);
	}

	public static File get2File(String url, Map body) {

		return httpRequestToFile(url, new HashMap<>(), GET, map2String(body));
	}

	public static File get2File(Map<String, String> headers, String url, String body) {

		return httpRequestToFile(url, headers, GET, body);
	}

	public static File get2File(Map<String, String> headers, String url, Map body) {

		return httpRequestToFile(url, headers, GET, map2String(body));
	}


	public static File get2File(Map<String, String> headers, String url) {

		return httpRequestToFile(url, headers, GET, null);
	}

	public static File get2File(String url) {

		return httpRequestToFile(url, new HashMap<>(), GET, null);
	}


	public static String post(String url, String body) {

		return httpRequestToString(url, new HashMap<>(), POST, body);
	}

	public static String post(String url, Map body) {

		return httpRequestToString(url, new HashMap<>(), POST, map2String(body));
	}

	public static String post(Map<String, String> headers, String url, String body) {

		return httpRequestToString(url, headers, POST, body);
	}

	public static String post(Map<String, String> headers, String url, Map body) {

		return httpRequestToString(url, headers, POST, map2String(body));
	}


	public static String post(Map<String, String> headers, String url) {

		return httpRequestToString(url, headers, POST, null);
	}

	public static String post(String url) {

		return httpRequestToString(url, new HashMap<>(), POST, null);
	}

	public static File post2File(String url, String body) {

		return httpRequestToFile(url, new HashMap<>(), POST, body);
	}


	public static File post2File(String url, Map body) {

		return httpRequestToFile(url, new HashMap<>(), POST, map2String(body));
	}


	public static File post2File(Map<String, String> headers, String url, String body) {

		return httpRequestToFile(url, headers, POST, body);
	}

	public static File post2File(Map<String, String> headers, String url, Map body) {

		return httpRequestToFile(url, headers, POST, map2String(body));
	}

	public static File post2File(Map<String, String> headers, String url) {

		return httpRequestToFile(url, headers, POST, null);
	}

	public static File post2File(String url) {

		return httpRequestToFile(url, new HashMap<>(), POST, null);
	}

	//同步end----------------

	private static void async(String path, Map<String, String> headers, String method, String body, Consumer task, boolean isFile) {

		getExecutorService().execute(() -> {
			if (isFile) {
				File file = httpRequestToFile(path, headers, method, body);
				if (task != null) {
					task.accept(file);
				}

			} else {
				String result = httpRequestToString(path, headers, method, body);
				if (task != null) {
					task.accept(result);
				}

			}
		});

	}

	//异步消费者start----------------
	public static void getAsync(String url, String body, Consumer task) {

		async(url, new HashMap<>(), GET, body, task, false);
	}

	public static void getAsync(String url, Map body, Consumer task) {

		async(url, new HashMap<>(), GET, map2String(body), task, false);
	}


	public static void getAsync(Map<String, String> headers, String url, String body, Consumer task) {

		async(url, headers, GET, body, task, false);
	}

	public static void getAsync(Map<String, String> headers, String url, Map body, Consumer task) {

		async(url, headers, GET, map2String(body), task, false);
	}

	public static void getAsync(Map<String, String> headers, String url, Consumer task) {

		async(url, headers, GET, null, task, false);

	}

	public static void getAsync(String url, Consumer task) {

		async(url, new HashMap<>(), GET, null, task, false);
	}

	public static void get2FileAsync(String url, String body, Consumer task) {

		async(url, new HashMap<>(), GET, body, task, true);
	}

	public static void get2FileAsync(String url, Map body, Consumer task) {

		async(url, new HashMap<>(), GET, map2String(body), task, true);

	}

	public static void get2FileAsync(Map<String, String> headers, String url, String body, Consumer task) {

		async(url, headers, GET, body, task, true);

	}

	public static void get2FileAsync(Map<String, String> headers, String url, Map body, Consumer task) {

		async(url, headers, GET, map2String(body), task, true);
	}


	public static void get2FileAsync(Map<String, String> headers, String url, Consumer task) {

		async(url, headers, GET, null, task, true);
	}

	public static void get2FileAsync(String url, Consumer task) {

		async(url, new HashMap<>(), GET, null, task, true);
	}


	public static void postAsync(String url, String body, Consumer task) {

		async(url, new HashMap<>(), POST, body, task, false);
	}

	public static void postAsync(String url, Map body, Consumer task) {

		async(url, new HashMap<>(), POST, map2String(body), task, false);
	}

	public static void postAsync(Map<String, String> headers, String url, String body, Consumer task) {

		async(url, headers, POST, body, task, false);
	}

	public static void postAsync(Map<String, String> headers, String url, Map body, Consumer task) {

		async(url, headers, POST, map2String(body), task, false);

	}


	public static void postAsync(Map<String, String> headers, String url, Consumer task) {

		async(url, headers, POST, null, task, false);
	}

	public static void postAsync(String url, Consumer task) {

		async(url, new HashMap<>(), POST, null, task, false);

	}

	public static void post2FileAsync(String url, String body, Consumer task) {

		async(url, new HashMap<>(), POST, body, task, true);
	}


	public static void post2FileAsync(String url, Map body, Consumer task) {

		async(url, new HashMap<>(), POST, map2String(body), task, true);

	}


	public static void post2FileAsync(Map<String, String> headers, String url, String body, Consumer task) {

		async(url, headers, POST, body, task, true);
	}

	public static void post2FileAsync(Map<String, String> headers, String url, Map body, Consumer task) {
		async(url, headers, POST, map2String(body), task, true);

	}

	public static void post2FileAsync(Map<String, String> headers, String url, Consumer task) {
		async(url, headers, POST, null, task, true);
	}

	public static void post2FileAsync(String url, Consumer task) {

		async(url, new HashMap<>(), POST, null, task, true);

	}

	//异步消费者end----------------


	//异步无消费者start----------------
	public static void getAsync(String url, String body) {

		async(url, new HashMap<>(), GET, body, null, false);
	}

	public static void getAsync(String url, Map body) {

		async(url, new HashMap<>(), GET, map2String(body), null, false);
	}


	public static void getAsync(Map<String, String> headers, String url, String body) {

		async(url, headers, GET, body, null, false);
	}

	public static void getAsync(Map<String, String> headers, String url, Map body) {

		async(url, headers, GET, map2String(body), null, false);
	}

	public static void getAsync(Map<String, String> headers, String url) {

		async(url, headers, GET, null, null, false);

	}

	public static void getAsync(String url) {

		async(url, new HashMap<>(), GET, null, null, false);
	}

	public static void get2FileAsync(String url, String body) {

		async(url, new HashMap<>(), GET, body, null, true);
	}

	public static void get2FileAsync(String url, Map body) {

		async(url, new HashMap<>(), GET, map2String(body), null, true);

	}

	public static void get2FileAsync(Map<String, String> headers, String url, String body) {

		async(url, headers, GET, body, null, true);

	}

	public static void get2FileAsync(Map<String, String> headers, String url, Map body) {

		async(url, headers, GET, map2String(body), null, true);
	}


	public static void get2FileAsync(Map<String, String> headers, String url) {

		async(url, headers, GET, null, null, true);
	}

	public static void get2FileAsync(String url) {

		async(url, new HashMap<>(), GET, null, null, true);
	}


	public static void postAsync(String url, String body) {

		async(url, new HashMap<>(), POST, body, null, false);
	}

	public static void postAsync(String url, Map body) {

		async(url, new HashMap<>(), POST, map2String(body), null, false);
	}

	public static void postAsync(Map<String, String> headers, String url, String body) {

		async(url, headers, POST, body, null, false);
	}

	public static void postAsync(Map<String, String> headers, String url, Map body) {

		async(url, headers, POST, map2String(body), null, false);

	}


	public static void postAsync(Map<String, String> headers, String url) {

		async(url, headers, POST, null, null, false);
	}

	public static void postAsync(String url) {

		async(url, new HashMap<>(), POST, null, null, false);

	}

	public static void post2FileAsync(String url, String body) {

		async(url, new HashMap<>(), POST, body, null, true);
	}


	public static void post2FileAsync(String url, Map body) {

		async(url, new HashMap<>(), POST, map2String(body), null, true);

	}


	public static void post2FileAsync(Map<String, String> headers, String url, String body) {

		async(url, headers, POST, body, null, true);
	}

	public static void post2FileAsync(Map<String, String> headers, String url, Map body) {
		async(url, headers, POST, map2String(body), null, true);

	}

	public static void post2FileAsync(Map<String, String> headers, String url) {
		async(url, headers, POST, null, null, true);
	}

	public static void post2FileAsync(String url) {

		async(url, new HashMap<>(), POST, null, null, true);

	}


	//异步无消费者start----------------

	public static String map2String(Map params) {
		StringBuilder data = new StringBuilder();

		for (Object param : params.entrySet()) {
			if (param instanceof Map.Entry) {
				Map.Entry entry = (Map.Entry) param;
				if (data.length() != 0) {
					data.append('&');
				}
				data.append(entry.getKey());
				data.append('=');
				data.append(entry.getValue());
			}

		}
		return data.toString();
	}


	private static String httpRequestToString(String path, Map<String, String> headers, String method, String body) {

		InputStreamReader inputStreamReader = null;
		BufferedReader bufferedReader = null;
		String response = "";
		URLConnection conn;
		InputStream inputStream = null;
		try {
			conn = baseHttpRequest(path, headers, method, body);

			if (conn == null) {
				return response;
			}
			inputStream = conn.getInputStream();
			inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
			bufferedReader = new BufferedReader(inputStreamReader);
			String str;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}

			response = buffer.toString();

			conn.getHeaderFields();

			convertHeader(conn.getHeaderFields(), headers);

		} catch (Exception e) {
			e.printStackTrace();

		} finally {


			try {
				if (bufferedReader != null) {

					bufferedReader.close();
				}
				if (inputStreamReader != null) {

					inputStreamReader.close();
				}
				if (inputStream != null) {

					inputStream.close();
				}
			} catch (IOException execption) {

			}
		}
		return response;
	}

	private static File httpRequestToFile(String path, Map<String, String> headers, String method, String body) {

		OutputStream os = null;
		BufferedInputStream bis = null;

		File tmpFile = null;
		URLConnection conn;
		InputStream inputStream = null;
		try {
			conn = baseHttpRequest(path, headers, method, body);
			if (conn == null) {
				return tmpFile;
			}

			String fileName = conn.getURL().getFile();
			if (fileName == null) {
				fileName = UUID.randomUUID().toString();
				tmpFile = File.createTempFile(fileName, fileName);
			} else {
				fileName = fileName.substring(fileName.lastIndexOf('/') + 1, fileName.lastIndexOf('?'));
				tmpFile = new File(System.getProperty("java.io.tmpdir"), fileName);
				if (!tmpFile.exists()) {
					tmpFile.createNewFile();
				}
			}


			inputStream = conn.getInputStream();
			bis = new BufferedInputStream((inputStream));
			os = new FileOutputStream(tmpFile);
			int len;
			byte[] arr = new byte[1024];
			while ((len = bis.read(arr)) != -1) {
				os.write(arr, 0, len);
				os.flush();
			}
			os.close();

			convertHeader(conn.getHeaderFields(), headers);

		} catch (Exception e) {

		} finally {

			try {
				os.close();
				bis.close();
				inputStream.close();
			} catch (IOException execption) {

			}
		}
		return tmpFile;
	}

	private static void convertHeader(Map<String, List<String>> sourceHeader
		, Map<String, String> targetHeader) {

		targetHeader.clear();
		StringBuilder builder;
		for (Map.Entry<String, List<String>> entry : sourceHeader.entrySet()) {
			if (entry.getKey() == null) {
				continue;
			}
			builder = new StringBuilder();
			for (String value : entry.getValue()) {

				if (builder.length() > 0) {
					builder.append(";");
				}

				builder.append(value);
			}
			targetHeader.put(entry.getKey(), builder.toString());

		}


	}


	private static URLConnection baseHttpRequest(String requestUrl, Map<String, String> headers,
                                                 String requestMethod, String outputStr) {
		try {
			SSLSocketFactory ssf = null;

			if (requestUrl.startsWith("https")) {
				TrustManager[] tm = {new X509TrustManager() {
					@Override
					public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

					}

					@Override
					public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

					}

					@Override
					public X509Certificate[] getAcceptedIssuers() {
						return new X509Certificate[0];
					}
				}};
				SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
				sslContext.init(null, tm, new java.security.SecureRandom());
				// 从上述SSLContext对象中得到SSLSocketFactory对象
				ssf = sslContext.getSocketFactory();

			}

			if (GET.equals(requestMethod)
				&& outputStr != null && !outputStr.isEmpty()) {
				requestUrl = requestUrl + "?" + outputStr;
				outputStr = null;
			}


			URL url = new URL(requestUrl);

			if (requestUrl.startsWith("https")) {
				HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
				conn.setSSLSocketFactory(ssf);
				// 设置请求方式（GET/POST）
				conn.setRequestMethod(requestMethod);
				return convertUrlConnection(conn, outputStr, headers);
			} else {
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				// 设置请求方式（GET/POST）
				conn.setRequestMethod(requestMethod);
				return convertUrlConnection(conn, outputStr, headers);
			}


		} catch (Exception ce) {
			ce.printStackTrace();
			return null;
		}

	}

	private static URLConnection convertUrlConnection(URLConnection conn, String outputStr, Map<String, String> headers) throws IOException {
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setUseCaches(false);
		// 设置连接超时
		conn.setConnectTimeout(5000);
		// 设置读取超时
		conn.setReadTimeout(5000);


		for (Map.Entry<String, String> entry : headers.entrySet()) {
			conn.setRequestProperty(entry.getKey(), entry.getValue());
		}

		if (null != outputStr && !outputStr.isEmpty()) {

			if (!outputStr.contains("&")) {
				//json
				conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
			}
			OutputStream outputStream = conn.getOutputStream();
			outputStream.write(outputStr.getBytes(StandardCharsets.UTF_8));
			outputStream.close();

		}
		return conn;
	}


}
